package ae.pcfc.etks.cld.service.dao.provider;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.LockMode;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;


/**
 * @author noor.haq
 * @description Extend your DAO with this generic DAO class in order to have
 *              basic CRUD operations support.
 */

public class GenericDAOImpl<T, ID extends Serializable> implements ae.pcfc.etks.cld.service.dao.GenericDAO<T, ID>
{

	private Class<T> clazz;
	private SessionFactory sessionFactory;
	
	@Autowired
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	@SuppressWarnings("unchecked")
	public GenericDAOImpl()
	{
		clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
		//this.sessionFactory = hibernateTemplate.getSessionFactory();
	}

	public void saveOrUpdate(T t)
			throws DataAccessException
	{
		this.sessionFactory.getCurrentSession().saveOrUpdate(t);
	}

	@SuppressWarnings("unchecked")
	public T load(ID id)
			throws DataAccessException
	{
		T load = (T) this.sessionFactory.getCurrentSession().load(clazz, id);
		return load;
	}

	@SuppressWarnings("unchecked")
	public T get(ID id)
			throws DataAccessException
	{
		T load = (T) this.sessionFactory.getCurrentSession().get(clazz, id);
		return load;
	}

	public boolean contains(T t)
			throws DataAccessException
	{
		return this.sessionFactory.getCurrentSession().contains(t);
	}

	public void delete(T t, LockMode lockMode)
			throws DataAccessException
	{
		this.delete(t, lockMode);
	}

	public void delete(T t)
			throws DataAccessException
	{
		this.sessionFactory.getCurrentSession().delete(t);
	}	

	public void deleteAll(Collection<T> entities)
			throws DataAccessException
	{
		this.deleteAll(entities);
	}

	@SuppressWarnings("unchecked")
	public List<T> find(String queryString, Object value)
			throws DataAccessException
	{
		List<T> find = (List<T>) this.find(queryString, value);
		return find;
	}

	@SuppressWarnings("unchecked")
	public List<T> find(String queryString, Object... values)
			throws DataAccessException
	{
		List<T> find = (List<T>) this.find(queryString, values);
		return find;
	}

	@SuppressWarnings("unchecked")
	public List<T> find(String queryString)
			throws DataAccessException
	{
		return (List<T>) this.find(queryString);
	}

	public void refresh(T t, LockMode lockMode)
			throws DataAccessException
	{
		this.refresh(t, lockMode);
	}

	public void refresh(T t)
			throws DataAccessException
	{
		this.refresh(t);
	}

	public Serializable save(T t)
			throws DataAccessException
	{
		return this.sessionFactory.getCurrentSession().save(t);
	}

	public void saveOrUpdateAll(Collection<T> entities)
			throws DataAccessException
	{
		this.saveOrUpdateAll(entities);
	}

	public void update(T t, LockMode lockMode)
			throws DataAccessException
	{
		this.update(t, lockMode);
	}

	public void update(T t)
			throws DataAccessException
	{
		this.sessionFactory.getCurrentSession().update(t);
	}

	@SuppressWarnings("unchecked")
	public List<T> list()
			throws DataAccessException
	{		
		return this.sessionFactory.getCurrentSession().createCriteria(clazz).list();

	}

	@SuppressWarnings("unchecked")
	public List<T> findByNamedQuery(String queryName)
			throws DataAccessException
	{
		return this.sessionFactory.getCurrentSession().getNamedQuery(queryName).list();
	}

	@SuppressWarnings("unchecked")
	public List<T> findByNamedQuery(String queryName, Object value)
			throws DataAccessException
	{
		return this.findByNamedQuery(queryName, value);
	}

	@SuppressWarnings("unchecked")
	public List<T> findByNamedQuery(String queryName, Object... values)
			throws DataAccessException
	{
		return this.findByNamedQuery(queryName, values);
	}

	@SuppressWarnings("unchecked")
	public List<T> findByCriteria(Criterion... criterion)
			throws DataAccessException
	{
		Criteria criteria = createCriteria();
		for (Criterion c : criterion)
			criteria.add(c);
		return criteria.list();
	}

	@SuppressWarnings("unchecked")
	public List<T> findByCriteria(ProjectionList projectionList, Criterion... criterion)
			throws DataAccessException
	{
		Criteria criteria = createCriteria();
		for (Criterion c : criterion)
			criteria.add(c);

		criteria.setProjection(projectionList);

		return criteria.list();
	}

	public int getCount()
			throws DataAccessException
	{
		Criteria criteria = createCriteria();
		criteria.setProjection(Projections.rowCount());
		return ((Integer) criteria.list().get(0)).intValue();
	}

	public int getCount(Criterion... criterion)
			throws DataAccessException
	{
		Criteria criteria = createCriteria();
		for (Criterion c : criterion)
			criteria.add(c);
		criteria.setProjection(Projections.rowCount());
		return ((Integer) criteria.list().get(0)).intValue();
	}

	public void deleteById(ID id)
			throws DataAccessException
	{
		Object obj = load(id);
		this.sessionFactory.getCurrentSession().delete((T) obj);
	}

	@SuppressWarnings("unchecked")
	public List<T> findByCriteria(Order[] orders, Criterion... criterion)
			throws DataAccessException
	{
		Criteria criteria = createCriteria();
		for (Criterion c : criterion)
			criteria.add(c);
		for (Order o : orders)
			criteria.addOrder(o);
		return criteria.list();
	}

	@SuppressWarnings("unchecked")
	public List<T> findByCriteria(Order[] orders, ProjectionList projectionList, Criterion... criterion)
			throws DataAccessException
	{
		Criteria criteria = createCriteria();
		for (Criterion c : criterion)
			criteria.add(c);
		criteria.setProjection(projectionList);
		for (Order o : orders)
			criteria.addOrder(o);
		return criteria.list();
	}
	
	@SuppressWarnings("unchecked")
	public List<T> findByCriteria(Order[] orders, ProjectionList projectionList,Class transformTo, Criterion... criterion)
			throws DataAccessException
	{
		Criteria criteria = createCriteria();
		for (Criterion c : criterion)
			criteria.add(c);
		criteria.setProjection(projectionList);
		for (Order o : orders)
			criteria.addOrder(o);
		
		criteria.setResultTransformer(Transformers.aliasToBean(transformTo));
		
		return criteria.list();
	}	
	

	@SuppressWarnings("unchecked")
	public T merge(T detachedInstance)
			throws DataAccessException
	{
		return (T) this.merge(detachedInstance);
	}

	@SuppressWarnings("unchecked")
	public List<T> findByExample(T exampleInstance, String... excludeProperty)
			throws DataAccessException
	{
		Criteria crit = createCriteria();
		Example example = Example.create(exampleInstance);
		for (String exclude : excludeProperty)
		{
			example.excludeProperty(exclude);
		}
		crit.add(example);
		return crit.list();
	}

	protected Criteria createCriteria()
	{
		return this.sessionFactory.getCurrentSession().createCriteria(clazz);
	}

	protected DetachedCriteria createDetachedCriteria()
	{
		return DetachedCriteria.forClass(clazz);
	}

	public void deleteByProperty(String property, Object value)
			throws DataAccessException {
		// TODO Auto-generated method stub
		
	}

	public int deleteAll() throws DataAccessException {
		// TODO Auto-generated method stub
		return 0;
	}

	public List<T> findByExample(T exampleInstance, MatchMode matchMode)
			throws DataAccessException {
		// TODO Auto-generated method stub
		return null;
	}

	public List<T> findByExample(T exampleInstance, MatchMode matchMode,
			String... excludeProperty) throws DataAccessException {
		// TODO Auto-generated method stub
		return null;
	}

	public List<T> findAllByProperty(String propertyName, Object value)
			throws DataAccessException {
		// TODO Auto-generated method stub
		return null;
	}

	public void flushAndClear() {
		// TODO Auto-generated method stub
		
	}
}
